package com.csy.filter;


import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference;

@Activate(group = {CommonConstants.CONSUMER})
public class MyTPMonitorFilter implements Filter {

    //key:String 方法名，Map<Long,Long>:每个方法的耗时统计（key：耗时时间，value:方法调用总次数，按照key自然排序）
    private static TreeMap<String,TreeMap<Long,Long>> methodTreeMap = new TreeMap<>(Comparator.naturalOrder());

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        long startTime = System.currentTimeMillis();
        Result result = invoker.invoke(invocation);

        long endTime = System.currentTimeMillis();
        // 方法耗时
        long costMills = endTime - startTime;
        // 方法名称
        String methodName = invocation.getMethodName();
        TreeMap<Long, Long> millsTreeMap = null;  //每个方法的耗时统计 key:耗时，value:该耗时内的调用次数
        if(methodTreeMap.containsKey(methodName)){
             millsTreeMap = methodTreeMap.get(methodName);
        }else {
            millsTreeMap = new TreeMap<>(Comparator.naturalOrder());
            methodTreeMap.put(methodName,millsTreeMap);
        }

        //如果treeMap中存在key即耗时时间,则value值即计数在原有基础上+1
        if(millsTreeMap.containsKey(costMills)){
            long value = millsTreeMap.get(costMills).longValue();
            millsTreeMap.replace(costMills,value+1);
        }else {
            millsTreeMap.put(costMills,1L);
        }
        //System.out.println("调用方法："+ methodName+"，耗时："+costMills+"毫秒");

        return result;
    }

    /**
     * 统计每个方法的TP90和TP99的耗时时间
     * @param method 方法名
     */
    public static void calcMethodTP(String method){

            TreeMap<Long, Long> treeMap;
            if (methodTreeMap.containsKey(method)) {
                treeMap = methodTreeMap.get(method);
            } else {
                System.out.println("treeMap 没有初始化");
                return;
            }

        synchronized (treeMap) {
            //根据耗时次数的treeMap来统计TP90 TP99
             TreeMap<Long, Long> transMap = new TreeMap<>(Comparator.naturalOrder());
        /*TP90: 方法总调用次数*90%
          TP99: 方法总调用次数*99%
          要计算TP90和TP99的耗时情况，首先得要统计出每个方法每个耗时的调用次数，然后将总次数*90%，得到TP90；
          那么要取出TP90对应的耗时值，则需要根据TP90去找到其对应的区间的耗时值，这里就将原来的TreeMap做一次key、value的转换，将耗时对应的调用次数作为key，将耗时作为value来存储
          如：
          方法A   1ms 2ms 3ms 50ms 100ms
          调用次数 10  100  50  10  10
          则：总次数：180，那么tp90=180*0.9=162  tp99=180*0.99=178.2
          key-value转换之后(key从0开始，通过AtomicReference生成，如果每次的key=上一个entryset的key+value)
          key:    0   0+10=10   10+100=110 160    170
          value: 1ms   2ms         3ms      50ms   100ms
          再通过TreeMap的floorEntry来返回 小于等于指定元素的最大值。
          那么tp90：162耗时时间就是160对应的50ms
              tp99:178.2>=170,就应该是100ms
          */
            //将入参的treeMap进行一次转换, 通过TreeMap的自然排序，将每次耗时的总次数作为作为key，将耗时作为value，以此来找出TP90 TP99对应的耗时情况
            AtomicReference<Long> idx = new AtomicReference<>(0l);
            treeMap.forEach((key, value) -> {
                transMap.put(idx.get(), key);
                idx.updateAndGet(v -> v + value);
            });

            Long total = treeMap.values().stream().mapToLong(o -> o).sum();
            Double idx90 = Math.ceil((double) total * 90 / 100);
            Double idx99 = Math.ceil((double) total * 99 / 100);
            Map.Entry<Long, Long> idx90Entry = transMap.floorEntry(idx90.longValue());
            Map.Entry<Long, Long> idx99Entry = transMap.floorEntry(idx99.longValue());

            Long tp90 = idx90Entry.getValue();
            Long tp99 = idx99Entry.getValue();

            System.out.println("====方法" + method + "的TP90：" + tp90 + "毫秒,TP99: " + tp99 + "毫秒");
        }
    }





}
